package com.example.activiti.service.impl;

import com.example.activiti.po.ActivitiPo;
import com.example.activiti.service.ActivitiService;


import com.fasterxml.jackson.annotation.JsonFormat;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstanceQuery;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;

@Service
public class ActivitiServiceImpl implements ActivitiService {

    private static final String PROCESS_NAME="lcfc-event.bpmn";

    private static final String PATH_PROCESS = "processes/";

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    TaskService taskService;

    @Autowired
    HistoryService historyService;


    @Autowired
    IEventInfoService eventInfoService;

    @Autowired
    SysUserService userService;

    //流程是否重新部署
    @Value("${spring.activiti.reload:false}")
    private Boolean reload;

    @Override
    public String startProcess(String owner) {
        Deployment deployment =  null;
        if (reload){//如果不重新部署，从数据库获取流程部署信息
            deployment = repositoryService.createDeploymentQuery().deploymentName(PROCESS_NAME).singleResult();

        }
        //如果没有获取到指定流程部署，重新再部署一个
        if (deployment==null){
            deployment = repositoryService.createDeployment()
                    .addClasspathResource(PATH_PROCESS+PROCESS_NAME).deploy();
        }
        //获取流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId()).singleResult();

        //开始设置流程参数
        Map<String,Object> variables = new HashMap<>();
        if (StringUtils.isBlank(owner)){
            throw new ActivitiException("请传入owner");
        }

        variables.put("owner",owner);
        //设置非直接结束
        variables.put("directEnd",0);
        //设置是否回退标志
        variables.put(ProcessUtils.BACK,0);

        //启动流程定义，返回流程实例
        ProcessInstance pi = runtimeService.startProcessInstanceById(processDefinition.getId(),variables);
        String processId = pi.getId();
        log.debug("流程创建成功，当前流程实例ID："+processId);
        return processId;
    }

    @Override
    public PageDataInfo getMyActivitiWait(String userId, Integer pageNo, Integer pageSize) {

        long total = taskService.createTaskQuery().taskAssignee(userId).count();
        long totoalPages = total/pageSize;
        Set<String> pids = new HashSet<>();
//        List<Task> tasks =
        taskService.createTaskQuery().taskAssignee(userId)
                .listPage(pageNo,pageSize).forEach(x->{
            pids.add(x.getProcessInstanceId());
        });
        List<ActivitiWaitPo> waitPoList = new ArrayList<>();
        if (pids.size()>0){
            eventInfoService.list(new QueryWrapper<EventInfo>().lambda()
                    .in(EventInfo::getProcessId,pids.toArray())).forEach(x->{
                ActivitiWaitPo po = ActivitiWaitPo.builder()
                        .title(x.getTitle())
                        .type(x.getType())
                        .processDesc(x.getProcessDesc())
                        .processStatus(x.getProcessStatus())
                        .owner(x.getOwner())
                        .processor(x.getProcessor())
                        .processStartTime(x.getProcessStartTime())
                        .processEndTime(x.getProcessEndTime())
                        .build();

                String pid = x.getProcessId();
                Task task = taskService.createTaskQuery().processInstanceId(x.getProcessId())
                        .taskAssignee(userId).singleResult();
                String taskId = task.getId();
                po.setPid(pid);
                po.setTaskId(taskId);
                po.setTaskNodeId(task.getTaskDefinitionKey());
                waitPoList.add(po);

            });
        }


        return PageDataInfo.builder()
                .records(waitPoList)
                .total(total)
                .totoalPages(totoalPages)
                .pageNum((long)pageNo)
                .pageSize(pageSize).build();







    }

    @Override
    public R approve(ActivitiPo entity) {
        String userId = SecurityUtils.getUserId();

        String username = SecurityUtils.getUsername();

        String pid = entity.getPid();

        String taskId = entity.getTaskId();

        TaskQuery query = taskService.createTaskQuery();
        if (StringUtils.isNoneBlank(pid)){
            query.processInstanceId(pid);
        }
        if (StringUtils.isNoneBlank(taskId)){
            query.taskId(taskId);
        }else {//如果为空，那么获取当前流程里面该人员处理节点
            taskId = taskService.createTaskQuery().processInstanceId(pid)
                    .taskAssignee(userId).singleResult().getId();
        }

        query.taskAssignee(userId);

        if (query.count()<=0){
            return R.fail("非节点创建人，没有权限");
        }

        if (StringUtils.isNoneBlank(entity.getMsg())){

            if (StringUtils.isBlank(pid)){
                pid = taskService.createTaskQuery().taskId(taskId).singleResult().getProcessInstanceId();
            }
            Authentication.setAuthenticatedUserId(username);
            taskService.addComment(taskId,pid,entity.getMsg());
        }
        if (StringUtils.isNoneBlank(entity.getProcessor())){
            //设置事件处理人
            taskService.setVariable(taskId,"processor",entity.getProcessor());
        }

        taskService.complete(entity.getTaskId());

        return R.ok();
    }

    @Override
    public R getHistoryLine(String pid) {
        HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery();
        query.processInstanceId(pid);

        List<ActivitiHistoryPo> historyPos = new ArrayList<>();

        Set<String> userIds = new HashSet<>();


        query.orderByHistoricActivityInstanceStartTime().asc().list().forEach(x->{
            ActivitiHistoryPo item = ActivitiHistoryPo.builder()
                    .activityId(x.getActivityId())
                    .assignee(x.getAssignee())
                    .createTime(x.getStartTime())
                    .build()
                    ;
            userIds.add(x.getAssignee());

            List<Comment> comments = taskService.getTaskComments(x.getTaskId());

            if (comments!=null&&comments.size()>0){
                StringBuilder itemComment = new StringBuilder();
                comments.forEach(itemCom->{
                    itemComment.append(itemCom.getFullMessage()).append(" ");
                });
                item.setMsg(itemComment.toString());
            }
            if (StringUtils.isNoneBlank(x.getDeleteReason())){
                item.setIsBack(InfoConstants.DeleteStatus.YES);

                item.setMsg(x.getDeleteReason());
            }else item.setIsBack(InfoConstants.DeleteStatus.NO);

            historyPos.add(item);
        });


        R<List<SysUser>> users = userService.getEmailsByUserIds(StringUtils.join(userIds,","));
        Map<String,String> userMap = new HashMap<>();
        if (users.getCode()==R.SUCCESS&&users.getData().size()>0){
            users.getData().forEach(x->{
                userMap.put(x.getIdNoSz(),x.getRealName());
            });
        }



        return transActivitiLine(historyPos,userMap);
    }

    /**
     * 封装流程流转线
     * @return
     */
    private R transActivitiLine(List<ActivitiHistoryPo> historyPos,Map<String,String> username){
        Map<String,Object> map = new HashMap<>();
        //触发
        historyPos.forEach(x->{
            if (x.getActivityId().equals(InfoConstants.ProcessNode.START_OWNER)){
                List<ActivitiLine> lines = new ArrayList<>();
                ActivitiLine line = new ActivitiLine();
                line.setFrom(username.get(x.getAssignee()));
                lines.add(line);
                map.put(InfoConstants.ProcessNode.START_OWNER,lines);
            }
        });

        //分配
        map.put(InfoConstants.ProcessNode.PROCESSOR,
                getTargetNodeLine(InfoConstants.ProcessNode.PROCESSOR,historyPos,username));

        //设置提交
        map.put(InfoConstants.ProcessNode.END_OWNER,
                getTargetNodeLine(InfoConstants.ProcessNode.END_OWNER,historyPos,username));



        return R.ok(map);

    }

    private List<ActivitiLine> getTargetNodeLine(String targetNodeId,
                                                 List<ActivitiHistoryPo> historyPos,Map<String,String> username){
        List<ActivitiLine> lines = new ArrayList<>();
        for (int i = 0; i < historyPos.size()-1; ) {
            ActivitiHistoryPo x = historyPos.get(i);
            if (x.getActivityId().equals(targetNodeId)){
                //设置上个节点信息
                ActivitiLine preLine = new ActivitiLine();
                ActivitiHistoryPo pre = getPre(historyPos,i-1);
                preLine.setTo(username.get(x.getAssignee()));
                if (pre!=null){
                    preLine.setFrom(username.get(pre.getAssignee()));
                    preLine.setStartTime(pre.getCreateTime());
                    preLine.setCurMsg(pre.getMsg());
                    preLine.setIsBack(pre.getIsBack());
                }
                preLine.setOtherMsg(x.getMsg());
                preLine.setEndTime(x.getCreateTime());
                lines.add(preLine);

                ActivitiHistoryPo next = getNext(historyPos,i+1);
                //设置下个节点信息
                ActivitiLine nextline = new ActivitiLine();
                nextline.setFrom(username.get(x.getAssignee()));
                nextline.setCurMsg(x.getMsg());
                nextline.setStartTime(x.getCreateTime());
                if (next!=null){
                    nextline.setOtherMsg(next.getMsg());
                    nextline.setEndTime(next.getCreateTime());
                    nextline.setTo(username.get(next.getAssignee()));

                }
                nextline.setIsBack(x.getIsBack());
                lines.add(nextline);

            }
            i++;
        }
        return lines;

    }

    /**
     * 获取历史中下个节点
     * @param poList
     * @param start
     * @return
     */
    private ActivitiHistoryPo getNext(List<ActivitiHistoryPo> poList,int start){
        ActivitiHistoryPo result = null;
        for (int i = start; i <poList.size() ; i++) {
            ActivitiHistoryPo next = poList.get(i);
            if (next.getActivityId().equals(InfoConstants.ProcessNode.PROCESSOR)
                    || next.getActivityId().equals(InfoConstants.ProcessNode.START_OWNER)
                    || next.getActivityId().equals(InfoConstants.ProcessNode.END_OWNER)){
                result = next;
                break;
            }
        }
        return  result;
    }

    /**
     * 获取上个节点
     * @param poList
     * @param start
     * @return
     */
    private ActivitiHistoryPo getPre(List<ActivitiHistoryPo> poList,int start){
        ActivitiHistoryPo result = null;
        for (int i = start; i >=0 ; i--) {
            ActivitiHistoryPo pre = poList.get(i);
            if (pre.getActivityId().equals(InfoConstants.ProcessNode.PROCESSOR)
                    || pre.getActivityId().equals(InfoConstants.ProcessNode.START_OWNER)
                    || pre.getActivityId().equals(InfoConstants.ProcessNode.END_OWNER)){
                result = pre;
                break;
            }
        }
        return  result;
    }

    class ActivitiLine implements Serializable {

        private static final long serialVersionUID = 1L;
        private String from;
        private String to;
        private int isBack;

        @JsonFormat(
                pattern = "yyyy-MM-dd HH:mm:ss",
                timezone = "GMT+8"
        )
        private Date startTime;

        @JsonFormat(
                pattern = "yyyy-MM-dd HH:mm:ss",
                timezone = "GMT+8"
        )
        private Date endTime;

        private String curMsg;

        private String otherMsg;

        public Date getStartTime() {
            return startTime;
        }

        public void setStartTime(Date startTime) {
            this.startTime = startTime;
        }

        public Date getEndTime() {
            return endTime;
        }

        public void setEndTime(Date endTime) {
            this.endTime = endTime;
        }

        public String getCurMsg() {
            return curMsg;
        }

        public void setCurMsg(String curMsg) {
            this.curMsg = curMsg;
        }

        public String getOtherMsg() {
            return otherMsg;
        }

        public void setOtherMsg(String otherMsg) {
            this.otherMsg = otherMsg;
        }

        public String getFrom() {
            return from;
        }

        public void setFrom(String from) {
            this.from = from;
        }

        public String getTo() {
            return to;
        }

        public void setTo(String to) {
            this.to = to;
        }

        public int getIsBack() {
            return isBack;
        }

        public void setIsBack(int isBack) {
            this.isBack = isBack;
        }
    }
}
